using System;
using System.Collections.Generic;
using System.Text;

namespace BadHabit.Factory  {


    public struct WorkingNowTaskInfo {
        public string TaskUId;
        public DateTime StartTime;
        public TimeSpan WorkedHoursSoFar {
            get {
                return DateTime.Now.Subtract(StartTime);
            }
        }
    }

    public partial class Factory {


        // This dictionary keeps track in memory of the task the each
        // user is currently working
        private Dictionary<string, WorkingNowTaskInfo> workingNowTasksByTeamMember = new Dictionary<string, WorkingNowTaskInfo>();

        private static Data.TaskDataSetTableAdapters.TaskTableAdapter taskTA = new BadHabit.Factory.Data.TaskDataSetTableAdapters.TaskTableAdapter();
        private static Data.TaskDataSetTableAdapters.TaskResumeTableAdapter taskResumeTA = new BadHabit.Factory.Data.TaskDataSetTableAdapters.TaskResumeTableAdapter();

        private static Data.TaskDataSetTableAdapters.AverageHoursBySizeItemTypeTableAdapter averageHoursTA = new Data.TaskDataSetTableAdapters.AverageHoursBySizeItemTypeTableAdapter();


        public Data.TaskDataSet RetriveAverageHoursBySizeItemType() {
            Data.TaskDataSet dsTask = new BadHabit.Factory.Data.TaskDataSet();
            averageHoursTA.Fill(dsTask.AverageHoursBySizeItemType);
            return dsTask;
        }


        /// <summary>
        /// Returns the current opened task of all team members.
        /// It agregate some EXTRA columns at the TASKRESUME table.
        /// Thats why the return typ isnot typed.
        /// </summary>
        /// <returns></returns>
        public System.Data.DataSet RetriveOpenedTasks() {
            Data.TaskDataSet tasks = new BadHabit.Factory.Data.TaskDataSet();
            taskResumeTA.FillOpened(tasks.TaskResume);
            MarkWorkingTasks(tasks);            
            System.Data.DataSet ds = new System.Data.DataSet();            
            ds.Merge(tasks);
            return ds;
        }


        private void MarkWorkingTasks(Data.TaskDataSet dsTask) {
            if (!dsTask.TaskResume.Columns.Contains("IsWorking"))
                dsTask.TaskResume.Columns.Add("IsWorking", typeof(bool));
            foreach (WorkingNowTaskInfo taskInfo in workingNowTasksByTeamMember.Values) {
                Data.TaskDataSet.TaskResumeRow task = dsTask.TaskResume.FindByTaskUId(taskInfo.TaskUId);
                if(task!=null)
                    task["IsWorking"] = true;
            }            
        }

    

       

        public Data.TaskDataSet SaveTask(Data.TaskDataSet task, out string alertMessage) {

            // validate tasks
            alertMessage = ValidateTask(task);
            if (alertMessage != null)
                return null;

            // tries to save it
            try {
                taskTA.Update(task);
            } catch (System.Data.DBConcurrencyException) {
                alertMessage = "Sorry, the task's data was changed by another user before you write it.";
            }

            return task;
        }

        public string ValidateTask(Data.TaskDataSet task) {

            // first get rid off the incomplete task rows
            System.Data.DataRow[] blankRows = task.Task.Select("isNull(TaskName,'')=''");
            foreach (System.Data.DataRow r in blankRows) {
                task.Task.Rows.Remove(r);
            }

            // auto fills the tasks id fields
            string msg = null;
        
            foreach (Data.TaskDataSet.TaskRow r in task.Task.Select("","projectUId")) {

                if (!r.IsTaskAssigneeNull() && !r.TaskAssignee.Equals(CurrentTeamMember) && !CurrentTeamMemberPrincipal.IsInRole(TeamRoles.SCRUM_MASTER.ToString()))
                    msg = msg + "\nYou can not change the task '" + r.TaskName + "' because it is not asssigned to you.";
                   

                // checks if the unit name was informed
                if (r.IsNull("UnitName")) {
                    // if there is no assignee cant define unit
                    if (r.IsTaskAssigneeNull()) {
                        msg = msg + "\nCould not define unit for task '" + r.TaskName + "'. Please define it manualy.";
                    }
                    else {
                        string unitName = GetDefaultUnitNameForAMember(r.TaskAssignee);
                        if (unitName == null)
                            msg = msg + "\nCould not define unit for task '" + r.TaskName + "'. Please define it manualy.";
                        else
                            r.UnitName = unitName;
                    }
                }

                
                // checks if some mess was made with the finish date for finish tasks
                if (r.IsFinishNull() && r.Status == 4) {
                    msg = msg + "\nYou should provided a finish date for the task '" + r.TaskName + "'.";
                }

            }
            return msg;

        }

        
        /// <summary>
        /// Retrive all tasks from the data base.
        /// </summary>        
        /// <returns>Task dataset</returns>
        public Data.TaskDataSet RetriveAllTasks() {
            Data.TaskDataSet task = new BadHabit.Factory.Data.TaskDataSet();
            taskTA.Fill(task.Task);
            return task;
        }

        /// <summary>
        /// Retrive tasks from a given project.
        /// </summary>        
        /// <param name="projectUId">Project UId</param>
        /// <param name="releaseNumber">The release number, use -1 for all releases</param>
        /// <returns>Task dataset</returns>
        public Data.TaskDataSet RetriveProjectTasks(string projectUId, short releaseNumber) {
            Data.TaskDataSet task = new BadHabit.Factory.Data.TaskDataSet();
            if(releaseNumber>=0)
                taskTA.FillByProjectUIdAndSprintNumber(task.Task, projectUId, releaseNumber);
            else
                taskTA.FillByProjectUId(task.Task, projectUId);
            return task;
        }


        public Data.TaskDataSet RetriveTask(int taskId) {
            Data.TaskDataSet task = new BadHabit.Factory.Data.TaskDataSet();
            taskAD.FillByTaskId(task.Task, taskId);
            return task;
        }

        


        /// <summary>
        /// Retrive tasks from a given project.
        /// </summary>        
        /// <param name="projectUId">Project UId</param>
        /// <param name="releaseNumber">The release number, use -1 for all releases</param>
        /// <returns>Task dataset</returns>
        public Data.TaskDataSet RetriveOpenedProjectTasks(string projectUId, short releaseNumber) {
            Data.TaskDataSet task = new BadHabit.Factory.Data.TaskDataSet();
            if(releaseNumber>=0)
                taskTA.FillOpenedByProjectUIdAndSprintNumber(task.Task, projectUId, releaseNumber);
            else
                taskTA.FillOpenedByProjectUId(task.Task, projectUId);
            return task;
        }

        /// <summary>
        /// Retrive tasks from a given project.
        /// </summary>        
        /// <param name="projectUId">Project UId</param>
        /// <param name="releaseNumber">The release number, use -1 for all releases</param>
        /// <returns>Task dataset</returns>
        public Data.TaskDataSet RetriveClosedProjectTasks(string projectUId, short releaseNumber) {
            Data.TaskDataSet task = new BadHabit.Factory.Data.TaskDataSet();
            if(releaseNumber>=0)
                taskTA.FillClosedByProjectUIdAndSprintNumber(task.Task, projectUId, releaseNumber);
            else
                taskTA.FillClosedByProjectUId(task.Task, projectUId);
            return task;
        }

        
        public Data.TaskDataSet RetriveDaillyProjectTasks(string projectUId) {
            Data.TaskDataSet task = new BadHabit.Factory.Data.TaskDataSet();

            // correct the weekend delay
            // we still hope that developers did not work on weekends
            decimal delay = 1;            
            if(DateTime.Today.DayOfWeek==DayOfWeek.Sunday)
                delay = 2;
            if (DateTime.Today.DayOfWeek == DayOfWeek.Monday)
                delay = 3;

            taskTA.FillDaillyByProjectUId(task.Task, projectUId, delay);
            return task;
        }



        public Data.TaskDataSet RetriveOpenedProjectTasksForUser(string projectUId, string taskAssignee, short releaseNumber) {
            Data.TaskDataSet task = new BadHabit.Factory.Data.TaskDataSet();
            if(releaseNumber>=0)
                taskTA.FillByProjectUIdAndAssigneeAndSprintNumber(task.Task, projectUId, taskAssignee, releaseNumber);
            else
                taskTA.FillByProjectUIdAndAssignee(task.Task, projectUId, taskAssignee);
            return task;
        }
        

        public Data.TaskDataSet.TaskResumeDataTable RetriveOpenedAssignedTasks(string taskAssignee) {
            Data.TaskDataSet.TaskResumeDataTable task = new BadHabit.Factory.Data.TaskDataSet.TaskResumeDataTable();                        
            taskResumeTA.FillOpenedByAssignee(task, taskAssignee);
            return task;
        }


        /// <summary>
        /// Retrives the amonu of tasks hours 
        /// </summary>
        /// <param name="taskAssignee"></param>
        /// <returns></returns>
        public double RetriveUserPlannedEffort(string taskAssignee) {
            object d = taskTA.ScalarUserPlannedEffort(taskAssignee);
            if (d == DBNull.Value || d == null)
                return 0;
            else
                return Double.Parse(d.ToString());
            
        }

        /// <summary>
        /// Retrives the amonu of tasks hours 
        /// </summary>
        /// <param name="taskAssignee"></param>
        /// <returns></returns>
        public double RetriveUserActualMonthEffort(string taskAssignee, int month, int year) {            
            object d = taskTA.ScalarUserActualMonthEffort((decimal)month, (decimal)year, taskAssignee);
            if (d == DBNull.Value || d == null)
                return 0;
            else
                return Double.Parse(d.ToString());
        }

        
        public string AddsActualEffort(string taskUId, double hours, bool closeIt) {

            if(IsIdleTask(taskUId))
                return String.Empty;

            // if should use round number
            if (Properties.Settings.Default.TaskMinCommitHours != 0) {
                // if is not a multiple
                if (hours % Properties.Settings.Default.TaskMinCommitHours != 0) {
                    int m = (int)(hours / Properties.Settings.Default.TaskMinCommitHours);
                    hours = (m * Properties.Settings.Default.TaskMinCommitHours) + Properties.Settings.Default.TaskMinCommitHours;
                }
            }

            if (hours < 0)
                hours = 0;
                        
            try {
                if (!closeIt) 
                    taskTA.AddsActualEffort((float)hours, taskUId);                             
                else
                    taskTA.CloseTask((float)hours, taskUId);

                // if the task's scenario was at the REQUIRED status, change it to WORKING
                taskTA.UpdateTaskBacklogItemStatus(taskUId);

            } catch (Exception e) {
                return e.Message;
            }
            return String.Empty;
        }

        /// <summary>
        /// Change the task that a user is working on.
        /// </summary>
        /// <param name="oldTaskUId">The UId of the task that the user was working</param>
        /// <param name="workedHours">The amount of worked hours</param>
        /// <param name="closeIt">True to close the task</param>
        /// <param name="newTaskUId">The UId of the new task </param>
        /// <param name="teamMember">The tem member perfoming the task</param>
        /// <returns></returns>
        public string ChangeTask(string oldTaskUId, double workedHours, bool closeIt, string newTaskUId, string teamMember) {
            string message = AddsActualEffort(oldTaskUId, workedHours, closeIt);

            if (String.Empty.Equals(message))
                KeepTrackWorkingTaskInfo(newTaskUId, teamMember.ToLower());
            
            return message;

        }

        private void KeepTrackWorkingTaskInfo(string taskUId, string teamMember) {

            // new task is idle
            if(IsIdleTask(taskUId)) {
                if (workingNowTasksByTeamMember.ContainsKey(teamMember.ToLower()))
                    workingNowTasksByTeamMember.Remove(teamMember.ToLower());
                return;
            }

            WorkingNowTaskInfo w = new WorkingNowTaskInfo();
            w.StartTime = DateTime.Now;
            w.TaskUId = taskUId;

            if (workingNowTasksByTeamMember.ContainsKey(teamMember.ToLower()))
                workingNowTasksByTeamMember[teamMember.ToLower()] = w;
            else
                workingNowTasksByTeamMember.Add(teamMember.ToLower(), w);
        }

        public string GetWorkingTaskUIdByTeamMember(string teamMember) {            
            if (!workingNowTasksByTeamMember.ContainsKey(teamMember.ToLower()))
                return null;
            return workingNowTasksByTeamMember[teamMember.ToLower()].TaskUId;
        }


        private bool IsIdleTask(string taskUId) {
            return (taskUId==null || taskUId.Trim().Equals(Guid.Empty.ToString()));
        }
        
    }
}
